package ru.pb.game.data.scripts;

import java.io.FileInputStream;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.log4j.Logger;

import ru.pb.global.configs.DevelopConfig;

/**
 * @author Felixx
 * @param <A>
 */
public abstract class JarHolder<A>
{
	protected final Logger _log = Logger.getLogger(getClass());
	protected Map<String, Class<A>> _classes = new HashMap<String, Class<A>>();

	protected void addClass(Class<A> a)
	{
		if(_classes.containsKey(a.getSimpleName()))
		{
			_log.info("Duplication name: " + a.getSimpleName());
			Thread.dumpStack();
			return;
		}
		_classes.put(a.getSimpleName(), a);

		if(DevelopConfig.DEBUG)
		{
			_log.info("Found " + a.getSimpleName());
		}
	}

	public Class<A> getClass(String name) throws ClassNotFoundException
	{
		Class<A> clazz = _classes.get(name);
		if(clazz == null)
		{
			throw new ClassNotFoundException("Not found: " + name);
		}
		return clazz;
	}

	/**
	 * @param p
	 */
	@SuppressWarnings("unchecked")
	protected final void load(String p)
	{
		for(ScriptClass clazz : ScriptLoader.getInstance().getAllClasses())
		{
			if(clazz != null && clazz.getSourceClass().getName().startsWith(p))
			{
				addClass((Class<A>) clazz.getSourceClass());
			}
		}
	}

	@SuppressWarnings("unchecked")
	protected final void load(String jarName, String packageName)
	{
		packageName = packageName.replaceAll("\\.", "/");

		if(DevelopConfig.DEBUG)
		{
			_log.info("Jar " + jarName + " looking for " + packageName);
		}
		try
		{
			JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
			JarEntry jarEntry;

			while((jarEntry = jarFile.getNextJarEntry()) != null)
			{
				if((jarEntry.getName().startsWith(packageName)) && (jarEntry.getName().endsWith(".class")))
				{
					if( !jarEntry.getName().replaceAll("/", "\\.").contains("$"))
					{
						Class<?> clazz = Class.forName(jarEntry.getName().replaceAll("/", "\\.").replace(".class", ""));

						if( !clazz.isEnum() && !Modifier.isAbstract(clazz.getModifiers()))
							addClass((Class<A>) clazz);
					}
				}
			}
		}
		catch(Exception e)
		{
			_log.info("Exception: " + e, e);
		}
	}

	public Collection<Class<A>> getClasses()
	{
		return _classes.values();
	}
}
